home *** CD-ROM | disk | FTP | other *** search
- # Copright 2008 Divmod, Inc. See LICENSE file for details.
- # -*- test-case-name: epsilon.test.test_expose -*-
-
- """
- This module provides L{Exposer}, a utility for creating decorators that expose
- methods on types for a particular purpose.
-
- The typical usage of this module is for an infrastructure layer (usually one
- that allows methods to be invoked from the network, directly or indirectly) to
- provide an explicit API for exposing those methods securely.
-
- For example, a sketch of a finger protocol implementation which could use this
- to expose the results of certain methods as finger results::
-
- # tx_finger.py
- fingermethod = Exposer("This object exposes finger methods.")
- ...
- class FingerProtocol(Protocol):
- def __init__(self, fingerModel):
- self.model = fingerModel
- ...
- def fingerQuestionReceived(self, whichUser):
- try:
- method = fingermethod.get(self.model, whichUser)
- except MethodNotExposed:
- method = lambda : "Unknown user"
- return method()
-
- # myfingerserver.py
- from tx_finger import fingermethod
- ...
- class MyFingerModel(object):
- @fingermethod.expose("bob")
- def someMethod(self):
- return "Bob is great."
-
- Assuming lots of protocol code to hook everything together, this would then
- allow you to use MyFingerModel and 'finger bob' to get the message 'Bob is
- great.'
- """
-
- import inspect
-
- from types import FunctionType
-
-
- class MethodNotExposed(Exception):
- """
- The requested method was not exposed for the purpose requested. More
- specifically, L{Exposer.get} was used to retrieve a key from an object
- which does not expose that key with that exposer.
- """
-
-
- class NameRequired(Exception):
- """
- L{Exposer.expose} was used to decorate a non-function object without having
- a key explicitly specified.
- """
-
-
-
- class Exposer(object):
- """
- This is an object that can expose and retrieve methods on classes.
-
- @ivar _exposed: a dict mapping exposed keys to exposed function objects.
- """
-
- def __init__(self, doc):
- """
- Create an exposer.
- """
- self.__doc__ = doc
- self._exposed = {}
-
-
- def expose(self, key=None):
- """
- Expose the decorated method for this L{Exposer} with the given key. A
- method which is exposed will be able to be retrieved by this
- L{Exposer}'s C{get} method with that key. If no key is provided, the
- key is the method name of the exposed method.
-
- Use like so::
-
- class MyClass:
- @someExposer.expose()
- def foo(): ...
-
- or::
-
- class MyClass:
- @someExposer.expose('foo')
- def unrelatedMethodName(): ...
-
- @param key: a hashable object, used by L{Exposer.get} to look up the
- decorated method later. If None, the key is the exposed method's name.
-
- @return: a 1-argument callable which records its input as exposed, then
- returns it.
- """
- def decorator(function):
- rkey = key
- if rkey is None:
- if isinstance(function, FunctionType):
- rkey = function.__name__
- else:
- raise NameRequired()
- if rkey not in self._exposed:
- self._exposed[rkey] = []
- self._exposed[rkey].append(function)
- return function
- return decorator
-
-
- def get(self, obj, key):
- """
- Retrieve 'key' from an instance of a class which previously exposed it.
-
- @param key: a hashable object, previously passed to L{Exposer.expose}.
-
- @return: the object which was exposed with the given name on obj's key.
-
- @raise MethodNotExposed: when the key in question was not exposed with
- this exposer.
- """
- if key not in self._exposed:
- raise MethodNotExposed()
- rightFuncs = self._exposed[key]
- T = obj.__class__
- seen = {}
- for subT in inspect.getmro(T):
- for name, value in subT.__dict__.items():
- for rightFunc in rightFuncs:
- if value is rightFunc:
- if name in seen:
- raise MethodNotExposed()
- return value.__get__(obj, T)
- seen[name] = True
- raise MethodNotExposed()
-